package com.ruge.excel.utils;

import com.ruge.date.DateUtil;
import com.ruge.excel.annotation.ExcelField;
import com.ruge.excel.converter.DefaultConvertible;
import com.ruge.excel.converter.WriteConvertible;
import com.ruge.excel.handler.ExcelHeader;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 爱丽丝、如歌  创建于 2018/10/16 15:23
 * 说明:  工具类
 */
public class Utils {
	/**
	 * 根据属性名与属性类型获取字段内容
	 *
	 * @param bean             对象
	 * @param fieldName        字段名
	 * @param writeConvertible 写入转换器
	 * @return 对象指定字段内容
	 */
	public static String getProperty (Object bean, String fieldName, WriteConvertible writeConvertible)
	{
		if (bean == null || fieldName == null) {
			throw new IllegalArgumentException("Operating bean or filed class must not be null");
		}
		Method method;
		Object object;
		try {
			method = getterOrSetter(bean.getClass(), fieldName, FieldAccessType.GETTER);
			object = method.invoke(bean);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		if (null != writeConvertible && writeConvertible.getClass() != DefaultConvertible.class) {
			// 写入转换器
			object = writeConvertible.execWrite(object);
		}
		return object == null ? "" : object.toString();
	}

	/**
	 * getter或setter枚举
	 */
	public enum FieldAccessType {

		GETTER, SETTER
	}

	/**
	 * 获取excel列表头
	 *
	 * @param titleRow excel行
	 * @param clz      类型
	 * @return ExcelHeader集合
	 */
	public static Map<Integer, ExcelHeader> getHeaderMap(Row titleRow, Class<?> clz) {
		List<ExcelHeader> headers = getHeaderList(clz);
		Map<Integer, ExcelHeader> maps = new HashMap<Integer, ExcelHeader>();
		for (Cell c : titleRow) {
			String title = c.getStringCellValue();
			for (ExcelHeader eh : headers) {
				if (eh.getTitle().equals(title.trim())) {
					maps.put(c.getColumnIndex(), eh);
					break;
				}
			}
		}
		return maps;
	}

	/**
	 * 根据JAVA对象注解获取Excel表头信息
	 *
	 * @param clz 类型
	 * @return 表头信息
	 */
	public static List<ExcelHeader> getHeaderList(Class<?> clz) {
		List<ExcelHeader> headers = new ArrayList<ExcelHeader>();
		List<Field> fields = new ArrayList<Field>();
		for (Class<?> clazz = clz; clazz != Object.class; clazz = clazz.getSuperclass()) {
			fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
		}
		for (Field field : fields) {
			// 是否使用ExcelField注解
			if (field.isAnnotationPresent(ExcelField.class)) {
				ExcelField er = field.getAnnotation(ExcelField.class);
				try {
					headers.add(new ExcelHeader(
							er.title(),
							er.order(),
							er.writeConverter().getDeclaredConstructor().newInstance(),
							er.readConverter().getDeclaredConstructor().newInstance(),
							field.getName(),
							field.getType()
					));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		Collections.sort(headers);
		return headers;
	}

	/**
	 * JDK 1.8+
	 * @param cell 单元格
	 * @return 单元格内容
	 */
	/*public static Object getCellValue(Cell cell) {
		Object obj = null;
		//把数字当成String来读，避免出现1读成1.0的情况
		if (StringUtils.equals("NUMERIC", cell.getCellType().name())) {
			cell.setCellType(CellType.STRING);
		}
		switch (cell.getCellType()) {
			case BOOLEAN:
				obj = cell.getBooleanCellValue();
				break;
			case ERROR:
				obj = cell.getErrorCellValue();
				break;
			case NUMERIC:
				obj = cell.getNumericCellValue();
				break;
			case STRING:
				obj = cell.getStringCellValue();
				break;
			default:
				break;
		}
		return obj;
	}*/

	/**
	 * JDK 1.6+
	 * @param cell
	 * @return
	 */
	public static String getCellValue(Cell cell) {
		String cellValue = null;
		//把数字当成String来读，避免出现1读成1.0的情况
		if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){
			cell.setCellType(Cell.CELL_TYPE_STRING);
		}
		//判断数据的类型
		switch (cell.getCellType()){
			case Cell.CELL_TYPE_NUMERIC: //数字
				cellValue = String.valueOf(cell.getNumericCellValue());
				break;
			case Cell.CELL_TYPE_STRING: //字符串
				cellValue = String.valueOf(cell.getStringCellValue());
				break;
			case Cell.CELL_TYPE_BOOLEAN: //Boolean
				cellValue = String.valueOf(cell.getBooleanCellValue());
				break;
			case Cell.CELL_TYPE_FORMULA: //公式
				cellValue = String.valueOf(cell.getCellFormula());
				break;
			case Cell.CELL_TYPE_BLANK: //空值
				cellValue = "";
				break;
			case Cell.CELL_TYPE_ERROR: //故障
				cellValue = "非法字符";
				break;
			default:
				cellValue = "未知类型";
				break;
		}
		return cellValue;
	}

	/**
	 * 字符串转对象
	 *
	 * @param strField 字符串
	 * @param clazz    待转类型
	 * @return 转换后数据
	 */
	public static Object getTargetClassByString(String strField, Class<?> clazz) {
		if (null == strField || "".equals(strField)) {
			return null;
		}
		if ((Long.class == clazz) || (long.class == clazz)) {
			strField = matchDoneBigDecimal(strField);
			strField = RegularUtils.converNumByReg(strField);
			return Long.parseLong(strField);
		}
		if ((Integer.class == clazz) || (int.class == clazz)) {
			strField = matchDoneBigDecimal(strField);
			strField = RegularUtils.converNumByReg(strField);
			return Integer.parseInt(strField);
		}
		if ((Float.class == clazz) || (float.class == clazz)) {
			strField = matchDoneBigDecimal(strField);
			return Float.parseFloat(strField);
		}
		if ((Double.class == clazz) || (double.class == clazz)) {
			strField = matchDoneBigDecimal(strField);
			return Double.parseDouble(strField);
		}
		if ((Character.class == clazz) || (char.class == clazz)) {
			return strField.toCharArray()[0];
		}
		if ((Boolean.class == clazz) || (boolean.class == clazz)) {
			return Boolean.parseBoolean(strField);
		}
		if (Date.class == clazz) {

			return DateUtil.getDateByString(strField);
		}
		return strField;
	}

	/**
	 * 科学计数法数据转换
	 *
	 * @param bigDecimal 科学计数法
	 * @return 数据字符串
	 */
	private static String matchDoneBigDecimal(String bigDecimal) {
		// 对科学计数法进行处理
		boolean flg = Pattern.matches("^-?\\d+(\\.\\d+)?(E-?\\d+)?$", bigDecimal);
		if (flg) {
			BigDecimal bd = new BigDecimal(bigDecimal);
			bigDecimal = bd.toPlainString();
		}
		return bigDecimal;
	}

	/**
	 * 根据属性名与属性类型获取字段内容
	 *
	 * @param bean  对象
	 * @param name  字段名
	 * @param value 字段类型
	 */
	public static void copyProperty(Object bean, String name, Object value) {
		if (null == name || null == value) {
			return;
		}
		Field field = matchClassField(bean.getClass(), name);
		if (null == field) {
			return;
		}
		Method method;
		try {
			method = getterOrSetter(bean.getClass(), name, FieldAccessType.SETTER);

			if (value.getClass() == field.getType()) {
				method.invoke(bean, value);
			} else {
				method.invoke(bean, getTargetClassByString(value.toString(), field.getType()));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 根据对象的属性名{@code fieldName}获取某个java的属性
	 *
	 * @param clazz     java对象的class属性
	 * @param fieldName 属性名
	 * @return java对象的属性
	 */
	private static Field matchClassField(Class clazz, String fieldName) {

		List<Field> fields = new ArrayList<Field>();
		for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
			fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
		}
		for (Field field : fields) {
			if (fieldName.equals(field.getName())) {
				return field;
			}
		}
		throw new IllegalArgumentException("[" + clazz.getName() + "] can`t found field with [" + fieldName + "]");
	}

	/**
	 * 根据java对象属性{@link Field}获取该属性的getter或setter方法名
	 *
	 * @param clazz      操作对象
	 * @param fieldName  对象属性
	 * @param methodType 方法类型，getter或setter枚举
	 * @return getter或setter方法
	 */
	public static Method getterOrSetter(Class clazz, String fieldName, FieldAccessType methodType) {

		Method result = null;
		if (null == fieldName || "".equals(fieldName)) {
			return null;
		}
		BeanInfo beanInfo = null;
		try {
			beanInfo = Introspector.getBeanInfo(clazz);
			PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor prop : props) {
				if (fieldName.equals(prop.getName())) {
					if (FieldAccessType.SETTER == methodType) {
						result = prop.getWriteMethod();
					}
					if (FieldAccessType.GETTER == methodType) {
						result = prop.getReadMethod();
					}
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("Can not get the getter or setter method");
		}
		return result;
	}
}
